﻿using System.Collections.Generic;
using System.Linq;
using DataScripter.Excel.FormattedValues;
using DataScripter.Excel.Formatters;
using DataScripter.Excel.Formulas;
using Microsoft.Office.Interop.Excel;

namespace DataScripter.Excel
{
	class Cell : ICell
	{
		private readonly IExcel _app;
		private Range _range;

		public Cell(IExcel app, string rangeIdentifier)
		{
			_app = app;
			_range = GetRange(rangeIdentifier);
		}

		private Range GetRange(string rangeIdentifier)
		{
			return (Range) _app.GetRange(rangeIdentifier);
		}

		private object Value
		{
			//get { return _range.Value2; }
			set { _range.Value2 = value; }
		}

		private object Formula
		{
			//get { return _range.Formula; }
			set { _range.Formula = value; }
		}

		public void AdvanceLine()
		{
			string newCellIdentifier = GetNextCellIdentifier(_range, 1, 0);
			_range = GetRange(newCellIdentifier);
		}

		private void AdvanceColumn()
		{
			string newCellIdentifier = GetNextCellIdentifier(_range, 0, 1);
			_range = GetRange(newCellIdentifier);
		}

		public ICell GetNextLine(int nrOfLines)
		{
			string newCellIdentifier = GetNextCellIdentifier(_range, nrOfLines, 0);
			return new Cell(_app, newCellIdentifier);
		}

        public ICell GetNextColumn(int nrOfColumns)
        {
            string newCellIdentifier = GetNextCellIdentifier(_range, 0, nrOfColumns);
            return new Cell(_app, newCellIdentifier);
        }

        private static string GetNextCellIdentifier(Range cursor, int rowDiff, int colDiff)
		{
			return (char)(64 + cursor.Column + colDiff) + (cursor.Row + rowDiff).ToString();
		}

		public string NumberFormat
		{
			set { _range.NumberFormat = value; }
		}

		public XlHAlign HorizontalAlignment
		{
			set { _range.HorizontalAlignment = value; }
		}

		public object Font
		{
			get { return _range.Font; }
		}

		public string CellReference
		{
			get { return GetNextCellIdentifier(_range, 0, 0); }
		}

		public string AbsoluteCellReference
		{
			get
			{
				string reference = CellReference;
				return "$" + reference.Substring(0, 1) + "$" + reference.Substring(1);
			}
		}

		public int Row
		{
			get { return _range.Row; }
		}
		public int Column
		{
			get { return _range.Column; }
		}

		public void SetAndAdvanceLine(object someValue, params object[] values)
		{
			SetAndAdvanceLine(new NoFormatter(), someValue, values);
		}

		public void SetAndAdvanceLine(ICellFormatter formatter, params object[] values)
		{
			SetAndAdvanceLine(formatter, values[0], values.Skip(1));
		}
		public void SetAndAdvanceLine<T>(ICellFormatter formatter, IEnumerable<T> values)
		{
            if (values == null)
            {
                AdvanceLine();
                return;
            }
			T first = values.First();
			IEnumerable<object> otherValues = values.Skip(1).OfType<object>();
			SetAndAdvanceLine(formatter, first, otherValues);
		}

		public void SetAndAdvanceLine(ICellFormatter formatter, object someValue, params object[] values)
		{
			SetAndAdvanceLine(formatter, someValue, (IEnumerable<object>)values);
		}

		public void SetAndAdvanceLine(ICellFormatter formatter, object someValue, IEnumerable<object> values)
		{
			formatter = formatter ?? new NoFormatter();

			Set(someValue);
			formatter.Format(this);

			Cell next = (Cell) GetNextColumn(1);
			foreach (object value in values)
			{
				next.Set(value);
				formatter.Format(next);

				next.AdvanceColumn();
			}
			AdvanceLine();
		}

		private void Set(object value)
		{
			IFormattedValue formattedValue = value as IFormattedValue;
			if (formattedValue != null)
			{
				Set(formattedValue.Value);
				formattedValue.Formatter.Format(this);
			}
			else
			{
				Formula formula = value as Formula;
				if (formula != null)
				{
					string excelFormula = formula.Resolve(this);
					Formula = "=" + excelFormula;
				}
				else
				{
					if (value == null)
						Value = null;
					else
					{
						Value = value.GetType().IsValueType ? value : value.ToString();
					}
				}
			}
		}

		public ICell GetDiff(int rowDiff, int colDiff)
		{
			string newCellIdentifier = GetNextCellIdentifier(_range, rowDiff, colDiff);
			return new Cell(_app, newCellIdentifier);
		}

		public ICell Clone()
		{
			return GetDiff(0, 0);
		}

		public override string ToString()
		{
			return CellReference;
		}

	}
}
